/*
 *  Copyright 2010 INFN - APE group
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#pragma once

#include <stdio.h>  // for fprintf
#include <cuda.h>
#include <cuqu/config.h>

#if !defined(CUQU_DISABLE_CUPRINTF)

#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 200
// here cuPrintf is used, temporarily placed in examples/
//#       warning "including cuPrintf.cu"
//#       include <utils/cuPrintf.cuh>
//#       define cudaPrintf cuPrintf
//#       define CUPRINTF_INCLUDED
#define cudaPrintf(FMT, ARGS...) /* cudaPrintf is disabled */
#else
// always include cuPrintf in host pass, even for SM>=20
//#       include <utils/cuPrintf.cuh>
#       define cudaPrintf printf
#endif

#else  // !defined(CUQU_DISABLE_CUPRINTF)

#warning "cuPrintf inclusion disabled"
#define cudaPrintf(FMT, ARGS...) /* cudaPrintf is disabled */

#endif // !defined(CUQU_DISABLE_CUPRINTF)

// GPU code debugging aids
#define cuqu_error(FMT, ARGS...) cudaPrintf(FMT, ##ARGS)

// enable trace prints
#if CUQU_ENABLE_TRACING
#define cuqu_trace(FMT, ARGS...) cudaPrintf(FMT, ##ARGS)
#else
#define cuqu_trace(FMT, ARGS...) do { } while(0)
#endif

// CPU code debugging aids
#define cuquError(FMT, ARGS...) fprintf(stderr, FMT, ##ARGS)

#define cuquErrorC(CNT, FMT, ARGS...) do {      \
        static int c = 0;                       \
        if(++c < (CNT))                         \
            cuquError(FMT, ##ARGS);             \
    } while(0)

#if CUQU_ENABLE_TRACING
#define cuquTrace(FMT, ARGS...) do { fprintf(stderr, FMT, ##ARGS); fflush(stdout); } while(0)
#else
#define cuquTrace(FMT, ARGS...) do { } while(0)
#endif

#define cuquTraceC(CNT, FMT, ARGS...) do {      \
        static int c = 0;                       \
        if(++c < (CNT))                         \
            cuquTrace(FMT, ##ARGS);             \
    } while(0)

// necessary on Fermi
#define cuqu_cast_to_ptr(TYPE, DEVPTR) ((TYPE*)(size_t)(DEVPTR))

/*
 * Code taken from NVIDIA Computing SDK
 */

#define cuquDrvSafeCall(A)         cuqu::__cuquDrvSafeCall(err, __FILE__, __LINE__)
#define cuquSafeCall(err)          cuqu::__cuquSafeCall(err, __FILE__, __LINE__)
#define cuquCheckMsg(msg)          cuqu::__cuquCheckMsg(msg, __FILE__, __LINE__)


namespace cuqu
{
    inline void __cuquDrvSafeCall( CUresult err, const char *file, const int line )
    {
        if( CUDA_SUCCESS != err) {
            fprintf(stderr, "%s(%i) : cuquDrvSafeCall() Driver API error : %04d.\n",
                    file, line, err);
            exit(-1);
        }
    }

    inline void __cuquSafeCall( cudaError err, const char *file, const int line )
    {
        if( cudaSuccess != err) {
            fprintf(stderr, "%s(%i) : cuquSafeCall() Runtime API error : %s.\n",
                     file, line, cudaGetErrorString(err));
            exit(-1);
        }
    }

    inline void __cuquCheckMsg( const char *errorMessage, const char *file, const int line )
    {
        cudaError_t err = cudaGetLastError();
        if( cudaSuccess != err) {
            fprintf(stderr, "%s(%i) : cuquCheckMsg() CUDA error : %s : %s.\n",
                     file, line, errorMessage, cudaGetErrorString( err) );
            exit(-1);
        }
#if defined(CUQU_DEBUG)
#pragma message "enabling cudaThreadSynchronize() in cuquCheckMsg()"
        err = cudaThreadSynchronize();
        if( cudaSuccess != err) {
            fprintf(stderr, "%s(%i) : cuquCheckMsg cudaThreadSynchronize error: %s : %s.\n",
                     file, line, errorMessage, cudaGetErrorString( err) );
            exit(-1);
        }
#endif
    }

    static bool isPow2(unsigned int x)
    {
        return ((x&(x-1))==0);
    }

    // a rough attempt at creating a delay function, with (optional) optimizer work-around
    __device__ int cuqu_delay_enabler = 0;
    __device__ int cuqu_delay_cnt = 0;
    static __device__ __noinline__ void cuqu_delay(int ncycles)
    {
        int v = clock();
        for(int c = 0; c < ncycles; ++c)
            v = v*v-c;

        if(cuqu_delay_enabler)
            cuqu_delay_cnt += v;
    }


    // from http://forums.nvidia.com/index.php?showtopic=197579
    static void dump_gpu_mem()
    {
        float free_m,total_m,used_m;
        size_t free_t,total_t;
        cudaMemGetInfo(&free_t,&total_t);
        free_m =(uint)free_t/1048576.0 ;
        total_m=(uint)total_t/1048576.0;
        used_m=total_m-free_m;
        printf ( "  mem free %d .... %f MB mem total %d....%f MB mem used %f MB\n",free_t,free_m,total_t,total_m,used_m);
    }

} // end namespace cuqu

/*
 * Local variables:
 *  mode: c++
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 *  indent-tabs-mode: nil
 * End:
 */
